/*
 * Copyright (C) 拾光分享网 
 * All rights reserved, Designed By www.hiai.top.
 * Email：goodsking@163.com.
 * Version：V1.0.2(20230701).
 */
package top.hiai.my.setting;

import com.jfinal.kit.HashKit;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.ehcache.CacheKit;
import com.jfinal.upload.UploadFile;
import top.hiai.common.constant.AppConstants;
import top.hiai.common.model.LoginLog;
import top.hiai.common.model.User;
import top.hiai.common.util.ImageKit;

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Date;

/**
 * @Author www.hiai.top
 * @Email goodsking@163.com
 * @Message 更多资源尽在www.hiai.top
 * @Introduction 类的介绍
 */

public class MySettingService {

    private User userDao = new User().dao();
    private LoginLog loginLogDao = new LoginLog().dao();
    /**
     * 经测试对同一张图片裁切后的图片 jpg为3.28KB，而 png 为 33.7KB，大了近 10 倍
     */
    public static final String EXT_NAME = ".jpg";
    private final String allAccountsCacheName = "allAccounts";
    private final String LoginUserCache = "index";

    /**
     * 获取登录用户的个人信息，用户资料修改
     *
     * @param uid
     * @return
     */
    public User getLoginUserMessage(int uid) {
        User user = userDao.findById(uid);
        user.removeSensitiveInfo();//去掉用户的密码
        return user;
    }

    /**
     * 修改用户的密码
     *
     * @param accountId
     * @param oldPassword
     * @param newPassword
     * @return
     */
    public Ret updatePassword(int accountId, String oldPassword, String newPassword, String newPasswordQue) {
        if (StrKit.isBlank(oldPassword)) {
            return Ret.fail().set("msg", "原密码不能为空");
        }
        if (StrKit.isBlank(newPassword)) {
            return Ret.fail().set("msg", "新密码不能为空");
        }
        if (newPassword.length() < AppConstants.USER_PASSWORD_LINE) {
            return Ret.fail().set("msg", "新密码长度不能小于 6");
        }
        if (StrKit.isBlank(newPasswordQue)) {
            return Ret.fail().set("msg", "确认密码不能为空");
        }
        if (!(newPassword.equals(newPasswordQue))) {
            return Ret.fail().set("msg", "两次密码不一样");
        }

        User account = userDao.findById(accountId);
        String salt = account.getSalt();
        String hashedPass = HashKit.sha256(salt + oldPassword);
        if (!hashedPass.equals(account.getPassword())) {
            return Ret.fail().set("msg", "原密码不正确，请重新输入");
        }

        salt = HashKit.generateSaltForSha256();
        newPassword = HashKit.sha256(salt + newPassword);
        int result = Db.update("update bbs_user set password=?, salt=? where uid=? limit 1", newPassword, salt, accountId);
        if (result > 0) {
            return Ret.ok("msg", "密码更新成功,请重新登录。");
        } else {
            return Ret.fail().set("msg", "未找到账号，请联系管理员");
        }
    }

    /**
     * 修改用户的基本资料
     *
     * @param accountId
     * @param newNickName
     * @param newSign
     * @return
     */
    public Ret profileSetting(int accountId, String newNickName, String newSign) {
        if (StrKit.isBlank(newNickName)) {
            return Ret.fail().set("msg", "昵称不能为空");
        }
        if (StrKit.isBlank(newSign)) {
            return Ret.fail().set("msg", "个性签名不能为空");
        }
        int result = Db.update(" update bbs_user set nickName=?, sign=? where uid=? limit 1", newNickName, newSign, accountId);

        if (result > 0) {
            return Ret.ok("msg", "资料更新成功。");
        } else {
            return Ret.fail().set("msg", "未找到账号，请联系管理员");
        }
    }

    /**
     * 保存认证的信息
     *
     * @param accountId
     * @param authName
     * @param trueName
     * @return
     */
    public Ret verifySetting(int accountId, String authName, String trueName) {
        if (StrKit.isBlank(authName)) {
            return Ret.fail().set("msg", "认证说明不能为空，");
        }
        if (authName.length() < AppConstants.USER_AUTH_NAME_LINE) {
            return Ret.fail().set("msg", "认证说明长度不能小于 10个字");
        }
        if (StrKit.isBlank(trueName)) {
            return Ret.fail().set("msg", "真实姓名不能为空");
        }
        Date authTime = new Date();
        int result = Db.update(" update bbs_user set authName=?, trueName=? ,authTime=? where uid=? limit 1", authName, trueName, authTime, accountId);

        if (result > 0) {
            return Ret.ok("msg", "认证提交成功，等待管理员的审核，谢谢。");
        } else {
            return Ret.fail().set("msg", "未找到账号，请联系管理员");
        }
    }

    /**
     * 获取用户的登录历史，分页显示
     *
     * @param pageNum
     * @param pageSize
     * @param accountId
     * @return
     */
    public Page<LoginLog> getUserLoginLogPage(int pageNum, int pageSize, int accountId) {
        String select = "select * ";
        String from = " from bbs_login_log where accountId=? order by addtime desc ";
        Page<LoginLog> loginLogList = loginLogDao.paginate(pageNum, pageSize, select, from, accountId);
        return loginLogList;
    }

    /**
     * 删除用户的登录历史
     *
     * @param id
     */
    public void deleteLoginLogById(int id) {
        loginLogDao.deleteById(id);
    }

    /**
     * 用户修改头像上传文件，以及上传后立即缩放后的文件暂存目录
     */
    public String getAvatarTempDir() {
        return "/avatar/temp/";
    }

    /**
     * 用户上传图像最多只允许 1M大小
     */
    public int getAvatarMaxSize() {
        return 1024 * 1024;
    }

    /**
     * 上传图像到临时目录，发回路径供 jcrop 裁切
     */
    public Ret uploadAvatar(int uid, UploadFile uf) {
        if (uf == null) {
            return Ret.fail().set("msg", "上传文件UploadFile对象不能为null");
        }
        try {
            if (ImageKit.notImageExtName(uf.getFileName())) {
                return Ret.fail().set("msg", "文件类型不正确，只支持图片类型：gif、jpg、jpeg、png、bmp");
            }
            String avatarUrl = "/upload" + getAvatarTempDir() + uid + "_" + System.currentTimeMillis() + EXT_NAME;
            String saveFile = PathKit.getWebRootPath() + avatarUrl;
            ImageKit.zoom(500, uf.getFile(), saveFile);
            return Ret.ok("avatarUrl", avatarUrl);
        } catch (Exception e) {
            return Ret.fail().set("msg", e.getMessage());
        } finally {
            uf.getFile().delete();
        }
    }

    /**
     * 保存 jcrop 裁切区域为用户头像
     *
     * @param
     * @param avatarUrl
     * @param x
     * @param y
     * @param width
     * @param height
     * @return
     */
    public Ret saveAvatar(User user, String avatarUrl, int x, int y, int width, int height) {
        int uid = user.getUid();
        /** 暂时用的 webRootPath，以后要改成 baseUploadPath，并从一个合理的地方得到 */
        String webRootPath = PathKit.getWebRootPath();
        String avatarFileName = webRootPath + avatarUrl;

        try {
            // 相对路径 + 文件名：用于保存到 account.avatar 字段
            String[] relativePathFileName = new String[1];
            // 绝对路径 + 文件名：用于保存到文件系统
            String[] absolutePathFileName = new String[1];
            buildPathAndFileName(uid, webRootPath, relativePathFileName, absolutePathFileName);

            BufferedImage bi = ImageKit.crop(avatarFileName, x, y, width, height);
            /** // 将 size 变为 200 X 200，resize 不会变改分辨率 */
            bi = ImageKit.resize(bi, 200, 200);
            deleteOldAvatarIfExists(absolutePathFileName[0]);
            ImageKit.save(bi, absolutePathFileName[0]);

            updateAccountAvatar(uid, relativePathFileName[0]);

            /**  首页的用户图片需要更新 */
            clearCache();
            return Ret.ok("msg", "头像更新成功，部分浏览器需要按 CTRL + F5 强制刷新看效果");
        } catch (Exception e) {
            return Ret.ok("msg", "头像更新成功，部分浏览器需要按 CTRL + F5 强制刷新看效果");
        } finally {
            /**  删除用于裁切的源文件 */
            new File(avatarFileName).delete();
        }
    }

    /**
     * 目前该方法为空实现
     * 如果在 linux 上跑稳了，此方法可以删除，不必去实现，如果出现 bug，
     * 则尝试实现该方法，即当用户图像存在时再次上传保存，则先删除老的，
     * 以免覆盖老文件时在 linux 之上出 bug
     */
    private void deleteOldAvatarIfExists(String oldAvatar) {

    }

    /**
     * 1：生成保存于 account.avatar 字段的：相对路径 + 文件名，存放于 relativePathFileName[0]
     * 2：生成保存于文件系统的：绝对路径 + 文件名，存放于 absolutePathFileName[0]
     * <p>
     * 3：用户头像保存于 baseUploadPath 之下的 /avatar/ 之下
     * 4：account.avatar 只存放相对于 baseUploadPath + "/avatar/" 之后的路径和文件名
     * 例如：/upload/avatar/0/123.jpg 只存放 "0/123.jpg" 这部分到 account.avatar 字段之中
     * <p>
     * 5："/avatar/" 之下生成的子录为 accountId 对 5000取整，例如 accountId 为 123 时，123 / 5000 = 0，生成目录为 "0"
     * 6：avatar 文件名为：accountId + ".jpg"
     */
    private void buildPathAndFileName(int uid, String webRootPath, String[] relativePathFileName, String[] absolutePathFileName) {
        String relativePath = (uid / 5000) + "/";
        String fileName = uid + EXT_NAME;
        relativePathFileName[0] = relativePath + fileName;
        /** webRootPath 将来要根据 baseUploadPath 调整，改代码，暂时选先这样用着，着急上线 */
        String absolutePath = webRootPath + "/upload/avatar/" + relativePath;
        File temp = new File(absolutePath);
        if (!temp.exists()) {
            /** 如果目录不存在则创建 */
            temp.mkdirs();
        }
        absolutePathFileName[0] = absolutePath + fileName;
    }

    /**
     * 更新用户的头像
     */
    public void updateAccountAvatar(int uid, String relativePathFileName) {
        Db.update("update bbs_user set avatar=? where uid=? limit 1", relativePathFileName, uid);
        clearCache(uid);
    }

    /**
     * 根据 uid 值移除缓存
     */
    public void clearCache(int uid) {
        CacheKit.remove(allAccountsCacheName, uid);
    }

    /**
     * 用户头像更新成功后刷新缓存
     */
    public void clearCache() {
        CacheKit.removeAll(LoginUserCache);
    }

}